/* eslint-disable */
/**
 * WebCodecs 工具类
 * 提供视频、音频和图像的解码功能以及媒体处理工具
 * 使用 @webav/av-cliper 库进行媒体操作，并实现内存缓存机制
 */

import type { VideoSource } from "@/class/VideoTrack";
import { baseFps } from "@/data/trackConfig";
import { MP4Clip, decodeImg, AudioClip, type IClip } from "@webav/av-cliper";
import { UnitFrame2μs } from "@/data/trackConfig";

/**
 * 内存缓存 Map
 * 存储文件ID到可读流的映射，避免重复读取相同文件
 */
const memoryCache = new Map<string, ReadableStream<Uint8Array>>();

/**
 * 处理文件流的读取和写入操作，仅使用内存缓存
 * @param id 文件唯一标识符
 * @param stream 可选的文件数据流
 * @returns 返回处理后的文件可读流
 * @throws 当未提供stream且缓存中不存在对应文件时抛出错误
 */
async function writeFile(id: string, stream?: ReadableStream<Uint8Array>) {
  try {
    // 如果没有传入 stream，尝试从内存缓存读取
    if (!stream) {
      const cached = memoryCache.get(id);
      if (cached) {
        return cached;
      }

      throw new Error(
        `stream is not provided and file not found in memory cache: ${id}`
      );
    }

    const start = performance.now();

    // 克隆 stream，因为 stream 只能读取一次
    const [cachedStream, returnStream] = stream.tee();

    // 存储到内存缓存
    memoryCache.set(id, cachedStream);
    console.log(
      "📦 使用内存缓存存储文件:",
      id,
      "耗时",
      performance.now() - start,
      "ms"
    );

    return returnStream;
  } catch (error) {
    console.error("❌ writeFile error:", error);

    // 如果提供了 stream，尝试直接返回
    if (stream) {
      return stream;
    }

    throw new Error(`Failed to get file stream for id: ${id}, error: ${error}`);
  }
}

/**
 * 从内存缓存中移除指定ID的文件
 * @param id 文件唯一标识符
 */
export function clearMemoryCacheById(id: string) {
  if (memoryCache.has(id)) {
    memoryCache.delete(id);
    console.log(`🗑️ Removed file from memory cache: ${id}`);
    return true;
  }
  return false;
}

/**
 * 清理内存缓存
 * 移除所有缓存的文件流，释放内存资源
 */
export function clearAllMemoryCache() {
  memoryCache.clear();
  console.log("🗑️ Memory cache all cleared");
}

/**
 * 获取缓存统计信息
 * @returns 返回缓存统计对象，包含缓存大小和键列表
 */
export function getCacheStats() {
  return {
    memoryCacheSize: memoryCache.size,
    memoryCacheKeys: Array.from(memoryCache.keys()),
  };
}

/**
 * 自定义视频解码器类
 * 处理视频解码、缩略图生成和帧提取功能
 */
class CustomVideoDecoder {
  /**
   * 存储视频ID到MP4Clip实例的映射
   * @private
   */
  #decoderMap = new Map<string, MP4Clip>();

  /**
   * 存储视频ID到缩略图列表的映射
   * @private
   */
  #thumbnailsMap = new Map<
    string,
    {
      img: Blob;
      ts: number;
    }[]
  >();

  /**
   * 生成视频缩略图
   * @param source 视频源对象，包含视频ID
   * @returns 返回缩略图数组，每个元素包含图片Blob和时间戳
   * @throws 当视频解码失败或clip未准备好时抛出错误
   */
  async thumbnails(source: VideoSource) {
    // 优先从缓存获取缩略图
    if (this.#thumbnailsMap.has(source.id)) {
      return this.#thumbnailsMap.get(source.id);
    }

    try {
      // 解码视频获取clip实例
      const clip = await this.decode({ id: source.id });

      if (!clip) {
        throw new Error("clip is not ready");
      }

      // 生成缩略图，每1秒一个
      const thumbnails = await clip.thumbnails(50, { step: 1e6 });
      // 缓存缩略图结果
      this.#thumbnailsMap.set(source.id, thumbnails);

      return thumbnails;
    } catch (error) {
      console.error("❌ 生成缩略图失败:", error);
      throw error;
    }
  }

  /**
   * 解码视频文件
   * @param id 视频文件唯一标识符
   * @param stream 可选的视频文件流
   * @param type 视频文件类型（可选）
   * @returns 返回解码后的MP4Clip实例
   * @throws 当解码失败时抛出错误
   */
  async decode({
    id,
    stream,
    type,
  }: {
    id: string;
    stream?: ReadableStream<Uint8Array>;
    type?: string;
  }) {
    // 优先从缓存获取解码器实例
    if (this.#decoderMap.has(id)) {
      return this.#decoderMap.get(id);
    }

    try {
      // 处理文件流
      stream = await writeFile(id, stream);

      // 创建MP4Clip实例并等待就绪
      const videoClip = new MP4Clip(stream);
      await videoClip.ready;

      // 缓存解码器实例
      this.#decoderMap.set(id, videoClip);

      return videoClip;
    } catch (error) {
      console.error("❌ 视频解码失败:", error);
      throw error;
    }
  }

  /**
   * 获取视频指定帧
   * @param id 视频文件唯一标识符
   * @param frameIndex 帧索引（从1开始）
   * @returns 返回指定索引的视频帧
   */
  async getFrame(id: string, frameIndex: number) {
    // 获取解码器实例，不存在则创建
    let clip = this.#decoderMap.get(id);
    if (!clip) {
      clip = await this.decode({ id });
    }

    // 计算帧时间戳，限定最小时间为5/30秒（避免前几帧为空）
    let time = Math.max(((frameIndex - 1) / baseFps) * 1e6, (5 / 30) * 1e6);
    // 使用tick方法获取指定时间的帧
    const frame = await (clip as MP4Clip).tick(time);

    return frame.video;
  }
}

/**
 * 自定义图像解码器类
 * 处理图像解码和帧提取功能
 */
class CustomImageDecoder {
  /**
   * 存储图像ID到VideoFrame数组的映射
   * @private
   */
  #decoderMap = new Map<string, VideoFrame[]>();

  /**
   * 解码图像文件
   * @param id 图像文件唯一标识符
   * @param stream 可选的图像文件流
   * @param type 图像文件类型
   * @returns 返回解码后的VideoFrame数组
   * @throws 当缺少type参数或解码失败时抛出错误
   */
  async decode({
    id,
    stream,
    type,
  }: {
    id: string;
    stream?: ReadableStream<Uint8Array>;
    type?: string;
  }) {
    console.log("🚀 ~ CustomImageDecoder ~ decode ~ id:", id);

    // 优先从缓存获取解码结果
    if (this.#decoderMap.has(id)) {
      return this.#decoderMap.get(id);
    }

    // 检查必要参数
    if (!type) {
      throw new Error("type is not ready");
    }

    try {
      // 处理文件流
      stream = await writeFile(id, stream);
      // 解码图像获取帧数组
      const frames = await decodeImg(stream, type);

      // 缓存解码结果
      this.#decoderMap.set(id, frames);

      return frames;
    } catch (error) {
      console.error("❌ 图片解码失败:", error);
      throw error;
    }
  }

  /**
   * 获取图像指定帧
   * @param type 图像文件类型
   * @param id 图像文件唯一标识符
   * @param frameIndex 帧索引
   * @returns 返回指定索引的VideoFrame
   */
  async getFrame(type: string, id: string, frameIndex: number) {
    // 获取解码结果，不存在则解码
    let frames = this.#decoderMap.get(id);
    if (!frames) {
      await this.decode({ id, type });
      frames = this.#decoderMap.get(id);
    }
    // 使用取模运算确保索引在有效范围内
    return frames?.[frameIndex % frames.length];
  }
}

/**
 * 自定义音频解码器类
 * 处理音频解码功能
 */
class CustomAudioDecoder {
  /**
   * 存储音频ID到AudioClip实例的映射
   * @private
   */
  #decoderMap = new Map<string, AudioClip>();

  /**
   * 解码音频文件
   * @param id 音频文件唯一标识符
   * @param stream 可选的音频文件流
   * @param type 音频文件类型
   * @returns 返回解码后的AudioClip实例
   * @throws 当缺少type参数或解码失败时抛出错误
   */
  async decode({
    id,
    stream,
    type,
  }: {
    id: string;
    stream?: ReadableStream<Uint8Array>;
    type?: string;
  }) {
    // 优先从缓存获取解码器实例
    if (this.#decoderMap.has(id)) {
      return this.#decoderMap.get(id);
    }

    // 检查必要参数
    if (!type) {
      throw new Error("type is not ready");
    }

    try {
      // 处理文件流
      stream = await writeFile(id, stream);

      // 创建AudioClip实例
      const clip = new AudioClip(stream);

      if (!clip) {
        throw new Error("解析音频失败");
      }

      // 等待音频解码器就绪
      await clip.ready;

      // 缓存解码器实例
      this.#decoderMap.set(id, clip);

      return clip;
    } catch (error) {
      console.error("❌ 音频解码失败:", error);
      throw error;
    }
  }
}

/**
 * 分割媒体片段
 * @param source 源媒体片段
 * @param offsetL 左侧偏移帧数
 * @param offsetR 右侧偏移帧数
 * @param frameCount 总帧数
 * @returns 返回分割后的媒体片段
 */
export const splitClip = async (
  source: IClip,
  {
    offsetL,
    offsetR,
    frameCount,
  }: { offsetL: number; offsetR: number; frameCount: number }
) => {
  try {
    // 如果无需分割，直接返回源
    if (offsetL === 0 && offsetR === 0) {
      return source;
    }
    
    // 确保source.ready已经完成
    if ('ready' in source && source.ready) {
      await source.ready;
    }
    
    // 计算起始时间（微秒）
    const start = offsetL * UnitFrame2μs;
    console.log(`splitClip - offsetL: ${offsetL}, start time: ${start}μs`);
    
    // 裁剪起始部分
    let clip;
    if (offsetL === 0) {
      clip = source;
    } else {
      try {
        const splitResult = await source.split(start);
        clip = splitResult[1]; // 取分割后的第二部分
      } catch (error) {
        console.error(`Failed to split at start time ${start}μs:`, error);
        // 如果分割失败，尝试使用整个源视频
        clip = source;
      }
    }
    
    // 计算结束时间（微秒）
    const end = (frameCount - offsetR - offsetL) * UnitFrame2μs;
    console.log(`splitClip - frameCount: ${frameCount}, offsetR: ${offsetR}, end time: ${end}μs`);
    
    // 裁剪结束部分
    if (offsetR === 0) {
      return clip;
    } else {
      try {
        const splitResult = await clip.split(end);
        return splitResult[0]; // 取分割后的第一部分
      } catch (error) {
        console.error(`Failed to split at end time ${end}μs:`, error);
        // 如果分割失败，返回当前clip
        return clip;
      }
    }
  } catch (error) {
    console.error('Error in splitClip:', error);
    // 发生严重错误时返回原始源
    return source;
  }
};

export const customVideoDecoder = new CustomVideoDecoder();

export const customImageDecoder = new CustomImageDecoder();

export const customAudioDecoder = new CustomAudioDecoder();
